#include "threadPool.h"

/* murmur_hash2 */
static uint32_t hash(const void* key, int len, uint32_t seed) {
    const uint32_t       m = 0x5bd1e995;
    const int            r = 24;
    uint32_t             h = seed ^ len;
    const unsigned char* data = (const unsigned char*)key;

    while (len >= 4) {
        uint32_t k = *(uint32_t*)data;
        k *= m;
        k ^= k >> r;
        k *= m;
        h *= m;
        h ^= k;
        data += 4;
        len -= 4;
    }

    switch (len) {
        case 3:
            h ^= data[2] << 16;
        case 2:
            h ^= data[1] << 8;
        case 1:
            h ^= data[0];
            h *= m;
    };

    h ^= h >> 13;
    h *= m;
    h ^= h >> 15;

    return h;
}

intTable* int_table_create() {
    intTable* h = (intTable*)calloc(1, sizeof(intTable));
    if (!h) {
        fprintf(stderr,
                "calloc intTable failed in intTable.c:int_table_init\n");
        return NULL;
    }
    int_hashNode** data =
        (int_hashNode**)calloc(CAPACITY_INIT, sizeof(int_hashNode*));
    if (!data) {
        fprintf(stderr, "calloc buckets failed in intTable.c:int_table_init\n");
        free(h);
        return NULL;
    }
    h->buckets = data;
    h->size = 0;
    h->capacity = CAPACITY_INIT;
    h->seed = time(NULL);
    return h;
}

static int int_table_grow(intTable* h) {
    int old_capacity = h->capacity;
    int new_capacity =
        old_capacity < 1024 ? 2 * old_capacity : 1.5 * old_capacity;
    int_hashNode** new_buckets =
        (int_hashNode**)calloc(new_capacity, sizeof(int_hashNode*));
    if (!new_buckets) {
        fprintf(stderr,
                "realloc buckets failed in intTable.c:int_table_grow\n");
        return -1;
    }
    uint32_t new_seed = time(NULL);
    for (int i = 0; i < old_capacity; i++) {
        int_hashNode* cur = h->buckets[i];
        while (cur) {
            uint32_t      hash_val = hash((void*)cur->key, sizeof(cur->key), new_seed);
            int           idx = hash_val % new_capacity;
            int_hashNode* tmp = cur->next;
            cur->next = new_buckets[idx];
            new_buckets[idx] = cur;
            cur = tmp;
        }
    }
    free(h->buckets);
    h->buckets = new_buckets;
    h->capacity = new_capacity;
    h->seed = new_seed;
    return 0;
}

int int_table_put(intTable* h, INT_TABLE_KEY_TYPE key, INT_TABLE_VAL_TYPE val) {
    if (h->size * 1.0 / h->capacity > 0.7) {
        if (int_table_grow(h) != 0) {
            fprintf(stderr, "intTable grow failed.\n");
            return -1;
        }
    }
    
    uint32_t      hash_val = hash((void*)key, sizeof(key), h->seed);
    int           index = hash_val % h->capacity;
    int_hashNode* cur = h->buckets[index];
    while (cur) {
        if (cur->key == key) {
            cur->val = val;
            return 0;
        }
        cur = cur->next;
    }
    int_hashNode* new_node = (int_hashNode*)calloc(1, sizeof(int_hashNode));
    new_node->key = key;
    new_node->val = val;
    new_node->next = h->buckets[index];
    h->buckets[index] = new_node;
    h->size++;
    return 0;
}

int int_table_del(intTable* h, INT_TABLE_KEY_TYPE key) {
    uint32_t      hash_val = hash((void*)key, sizeof(key), h->seed);
    int           index = hash_val % h->capacity;
    int_hashNode* cur = h->buckets[index];
    int_hashNode* prev = NULL;
    while (cur) {
        if (cur->key == key) {
            if (prev == NULL) {
                h->buckets[index] = cur->next;
            } else {
                prev->next = prev->next->next;
            }
            free(cur);
            h->size--;
            return 0;
        }
        prev = cur;
        cur = cur->next;
    }
    return -1;
}

int int_table_find(intTable* h, INT_TABLE_KEY_TYPE key,
                   INT_TABLE_VAL_TYPE* val) {

    uint32_t      hash_val = hash((void*)key, sizeof(key), h->seed);
    int           index = hash_val % h->capacity;
    int_hashNode* cur = h->buckets[index];
    while (cur) {
        if (cur->key == key) {
            *val = cur->val;
            return 0;
        }
        cur = cur->next;
    }
    return -1;
}
